package tools

/*
#include <stdlib.h>
#include <sys/sysctl.h>
#include <sys/mount.h>
*/
import "C"

import (
	"fmt"
	"os/exec"
	"strconv"
	"strings"
	"sync"
)

type CpuInfo struct {
	Family    int64
	Model     int64
	ModelName string
	MHz       float64
	CacheSize int32
}

type CPUTimesStat struct {
	CPU       string
	User      float64
	System    float64
	Idle      float64
	Nice      float64
	Iowait    float64
	Irq       float64
	Softirq   float64
	Steal     float64
	Guest     float64
	GuestNice float64
	Stolen    float64
}

type lastPercent struct {
	sync.Mutex
	lastCPUTimes    []TimesStat
	lastPerCPUTimes []TimesStat
}

var lastCpuPercent lastPercent

func init() {
	lastCPUPercent.Lock()
	lastCPUPercent.lastCPUTimes, _ = Times(false)
	lastCPUPercent.lastPerCPUTimes, _ = Times(true)
	lastCPUPercent.Unlock()
}

func (c *CpuInfo) GetInfo() {
	out, err := exec.Command("cat", "/proc/cpuinfo").Output()
	if err != nil {
		fmt.Println(err)
	}

	for _, line := range strings.Split(string(out), "\n") {
		values := strings.Fields(line)
		if len(values) < 1 {
			continue
		}

		switch {
		case strings.HasPrefix(line, "model		"):
			if modelValue, err := strconv.Atoi(strings.Replace(strings.Join(values[1:], ""), ":", "", -1)); err == nil {
				c.Model = int64(modelValue)
			}
		case strings.HasPrefix(line, "model name"):
			c.ModelName = strings.Replace(strings.Join(values[1:], ""), ":", "", -1)
		case strings.HasPrefix(line, "cpu family"):
			if modelValue, err := strconv.Atoi(strings.Replace(strings.Join(values[1:], ""), ":", "", -1)); err == nil {
				c.Family = int64(modelValue)
			}
		case strings.HasPrefix(line, "cpu MHz"):
			if modelValue, err := strconv.Atoi(strings.Replace(strings.Join(values[1:], ""), ":", "", -1)); err == nil {
				fmt.Println(modelValue)
				c.MHz = float64(modelValue)
			}
		case strings.HasPrefix(line, "cache_size"):
			if modelValue, err := strconv.Atoi(strings.Replace(strings.Join(values[1:], ""), ":", "", -1)); err == nil {
				fmt.Println(modelValue)
				c.CacheSize = modelValue
			}
		}

	}
}

func (c CpuInfo) ShowInfo() {
	fmt.Printf("the cpu model is: %d\n", c.Model)
	fmt.Printf("the cpu model name is: %s\n", c.ModelName)
	fmt.Printf("the cpu family is: %d\n", c.Family)
	fmt.Printf("the cpu MHz is: %f\n", c.MHz)
	fmt.Printf("the cpu MHz is: %f\n", c.CacheSize)
}

func Counts(logical bool) (int, error) {
	return runtime.NumCPU(), nil
}

func (c CPUTimesStat) String() string {
	v := []string{
		`"cpu":"` + c.CPU + `"`,
		`"user":` + strconv.FormatFloat(c.User, 'f', 1, 64),
		`"system":` + strconv.FormatFloat(c.System, 'f', 1, 64),
		`"idle":` + strconv.FormatFloat(c.Idle, 'f', 1, 64),
		`"nice":` + strconv.FormatFloat(c.Nice, 'f', 1, 64),
		`"iowait":` + strconv.FormatFloat(c.Iowait, 'f', 1, 64),
		`"irq":` + strconv.FormatFloat(c.Irq, 'f', 1, 64),
		`"softirq":` + strconv.FormatFloat(c.Softirq, 'f', 1, 64),
		`"steal":` + strconv.FormatFloat(c.Steal, 'f', 1, 64),
		`"guest":` + strconv.FormatFloat(c.Guest, 'f', 1, 64),
		`"guestNice":` + strconv.FormatFloat(c.GuestNice, 'f', 1, 64),
		`"stolen":` + strconv.FormatFloat(c.Stolen, 'f', 1, 64),
	}

	return `{` + strings.Join(v, ",") + `}`
}

func allCPUTimes() ([]TimesStat, error) {
	var count C.mach_msg_type_number_t
	var cpuload C.host_cpu_load_info_data_t

	count = C.HOST_CPU_LOAD_INFO_COUNT

	status := C.host_statistics(C.host_t(C.mach_host_self()),
		C.HOST_CPU_LOAD_INFO,
		C.host_info_t(unsafe.Pointer(&cpuload)),
		&count)

	if status != C.KERN_SUCCESS {
		return nil, fmt.Errorf("host_statistics error=%d", status)
	}

	c := TimesStat{
		CPU:    "cpu-total",
		User:   float64(cpuload.cpu_ticks[C.CPU_STATE_USER]) / ClocksPerSec,
		System: float64(cpuload.cpu_ticks[C.CPU_STATE_SYSTEM]) / ClocksPerSec,
		Nice:   float64(cpuload.cpu_ticks[C.CPU_STATE_NICE]) / ClocksPerSec,
		Idle:   float64(cpuload.cpu_ticks[C.CPU_STATE_IDLE]) / ClocksPerSec,
	}

	return []TimesStat{c}, nil

}
